package com.huilan.base.esl.service.acd.domain.model.phoneinline;
import com.huilan.base.esl.service.acd.domain.model.ACDException;
import com.huilan.base.esl.service.acd.domain.model.agent.Agent;
import com.huilan.base.esl.service.acd.domain.model.phoneinline.acd.IPhoneinlineACD;
import com.huilan.base.esl.service.acd.domain.model.phoneinline.acd.Wait;
import com.huilan.base.esl.service.acd.domain.model.phoneinline.acd.WaitACD;
import com.huilan.base.esl.service.acd.domain.model.queue.Queue;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.api.annotation.REntity;
import org.redisson.api.annotation.RId;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.huilan.base.esl.service.acd.domain.model.Base.getRedisson;

@Slf4j
@Getter
@Setter
@REntity
public class PhoneInline{

    @RId
    private String phone;
    // 通道
    private Channel channel;
    // 技能组
    private Queue queue ;
    // 坐席
    private Agent agent ;
    // acd
    private static IPhoneinlineACD phoneinlineACD = new WaitACD();

    private static String blockKey = "temp:phoneInline:block:";
    private static String lockKey = "temp:phoneInline:lock:";

    public static RCountDownLatch getBlock(String phone) {
        return getRedisson().getCountDownLatch(blockKey + phone);
    }
    public static RSemaphore getlock(String phone) {
        RSemaphore semaphore = getRedisson().getSemaphore(lockKey + phone);
        return semaphore;
    }
    public static void initLock(String phone){
        RSemaphore sp = getlock(phone);
        sp.trySetPermits(1);
    }
    public static boolean lock(String phone){
        RSemaphore sp = getlock(phone);
        boolean b = sp.tryAcquire();
        log.info("phone:{} 锁定 当前用户  num:{}",phone,sp.availablePermits());
        return b;
    }
    public boolean noLock(){
        RSemaphore sp = getlock(getPhone());
        int permits = sp.availablePermits();
        return 1 == permits;
    }
    public void unLock(String phone){
        RSemaphore sp = getlock(phone);
        sp.release();
        log.info("phone:{} 解锁 当前用户  num:{}",phone,sp.availablePermits());
    }
    public void delLock(String phone){
        getRedisson().getBucket(lockKey + phone).delete();
    }
    // 用户排队等待
    public void await(long timeout, TimeUnit unit,String phone){
        try {
            RCountDownLatch block = getBlock(phone);
            block.trySetCount(1);
            block.await(timeout, unit);
        }catch (Exception e){
            log.error("phone:{} 用户等待异常",phone,e);
        }
    }

    public PhoneInline() {
    }

    public PhoneInline(String phone) {
        this.phone = phone;
    }

    private PhoneInline(String phone, Channel channel) {
        this.phone = phone;
        this.channel = channel;
    }

    public static PhoneInline create(String phone, Channel channel, Queue queue){
        getRedisson().getBucket(blockKey + phone).delete();
        getRedisson().getBucket(lockKey + phone).delete();
        Wait.delete(phone);

        RLiveObjectService liveObjectService = getRedisson().getLiveObjectService();
        PhoneInline phoneInline = get(phone);
        if (phoneInline != null){
            liveObjectService.delete(phoneInline);
        }

        PhoneInline persist = liveObjectService.persist(new PhoneInline(phone, channel));
        persist.setQueue(queue);
        initLock(phone);
        phoneinlineACD.init(phone);
        return persist;
    }

    public static PhoneInline get(String phone){
        RLiveObjectService liveObjectService = getRedisson().getLiveObjectService();
        return liveObjectService.get(PhoneInline.class, phone);
    }

    // 选择一个排队的用户
    public static PhoneInline selectAQueuedUser(List<PhoneInline> phoneInlines){
        List<PhoneInline> phoneInlinesNoLock = phoneInlines = phoneInlines.stream().filter(s -> s.noLock()).collect(Collectors.toList());
        if (phoneInlines == null || phoneInlines.size() == 0){
            return null;
        }
        log.info("排队用户 size:{}",phoneInlinesNoLock.size());
        PhoneInline phoneInline = phoneinlineACD.choose(phoneInlinesNoLock);

        String phone = phoneInline.getPhone();
        boolean lock = lock(phone);
        if (lock){
            return phoneInline;
        }else{
            return selectAQueuedUser(phoneInlines);
        }
    }
    public void hangup(String phone){
        RCountDownLatch block = getBlock(phone);
        setAgent(null);
        block.countDown();
    }
    public void destroy(String phone){
        log.info("phone:{} 销毁",phone);
        delLock(phone);
        getQueue().delPhoneInline(this);
        Wait.delete(phone);
        RLiveObjectService liveObjectService = getRedisson().getLiveObjectService();
        liveObjectService.delete(this);
    }
    public void transfer() {
        String phone = getPhone();
        log.info("phone:{} begin ----------------------------------------- ",phone);
        log.info("phone:{} 开始转接 ",phone);
        Agent agent = null;
        try {
            log.info("phone:{} 查找可用坐席 ",phone);
            agent = getQueue().chooseAgent();
            log.info("phone:{} 坐席已选中 agent:{}",phone,agent.getExt());
            lock(phone);
        }catch (ACDException e){
            log.info("phone:{} 查找可用坐席 异常 :{}",phone,e.getMsg());
            agent = null;
        }

        if (agent == null){
            log.info("phone:{} 排队中",phone);
            getQueue().addPhoneInline(this);
            setAgent(null);
            await(10, TimeUnit.MINUTES,phone);
            agent = getAgent();
            if (agent == null){
                log.info("phone:{} 已经挂了 不用转接了",phone);
                destroy(phone);
                return;
            }
            log.info("phone:{} 排队成功 agent:{}",phone,agent.getExt());
        }

        if (agent != null){
            boolean transfer;
            try {
                log.info("phone:{} 转接 agent:{}",phone,agent.getExt());
                transfer = agent.transfer(this);
            }catch (ACDException e){
                destroy(phone);
                agent.online(true);
                return;
            }
            if (transfer == false){
                log.info("phone:{} 转接失败 继续转接",phone);
                getQueue().delPhoneInline(this);
                unLock(phone);
                transfer();
            }else{
                log.info("phone:{} 转接成功",phone);
                destroy(phone);
            }
        }

    }

}


